package main

import (
	"fmt"
	"time"
)

type Pool struct {
	// 不带缓冲的 chan，封装 work
	work chan func()

	// 带缓冲的 chan，代表 pool size
	sem chan int
}

func (p *Pool) NewTask4(task func()) {
	select {
	case p.work <- task:
		fmt.Println("开始执行")
	case p.sem <- 0:
		fmt.Println("开启协程了")
		go p.run4(task)
	}
}

// NewTask3 这种写法是有问题的，会造成死锁
func (p *Pool) NewTask3(task func()) {
	select {
	case p.work <- task:
		// work 不带缓冲区，所以会阻塞在这里
		fmt.Println("执行 case p.work")
	case p.sem <- 0:
		// sem 带缓冲区，所以会继续执行
		fmt.Println("执行 case p.sem")
		f := <-p.work
		f()
	}
}

func (p *Pool) NewTask2(task func()) {
	select {
	case p.work <- task:
		// work 不带缓冲区，所以会阻塞在这里
		fmt.Println("开始执行")
	case p.sem <- 0:
		// sem 带缓冲区，所以会继续执行
		fmt.Println("开启协程了")
		go p.run2()
	}
}

func (p *Pool) run4(task func()) {
	defer func() {
		fmt.Println("p.sem read")
		<-p.sem
	}()

	for {
		task()
		task = <-p.work
	}
}

func (p *Pool) run2() {
	f := <-p.work
	f()
	//defer func() {
	//	fmt.Println("close p.sem")
	//	<-p.sem
	//}()

	//for {
	//	f := <-p.work
	//	f()
	//}
}

func (p *Pool) NewTask(task func()) {
	select {
	// 由于 work chan 不带缓冲，所以会阻塞到这里
	case p.work <- task:
		fmt.Println("开始工作了")
	// sem 带缓冲，所以 0 能够塞进去，这个 chan ready 了，能够读到数据
	case p.sem <- 0:
		fmt.Println("开启协程了")
		go p.run(task)
	}
}

func (p *Pool) run(task func()) {
	defer func() { <-p.sem }()

	for {
		task()
		task = <-p.work
	}
}

func NewPool(n int) *Pool {
	return &Pool{
		work: make(chan func()),
		sem:  make(chan int, n),
	}
}

func main() {
	pool := NewPool(2)

	for i := 0; i < 5; i++ {
		pool.NewTask4(func() {
			time.Sleep(2 * time.Second)
			fmt.Println(time.Now())
		})
	}

	//pool.NewTask3(func() {
	//	fmt.Println(time.Now())
	//})

	// 保证所有的协程都执行完毕
	time.Sleep(5 * time.Second)
}
